# == Schema Information
# Schema version: 11
#
# Table name: users
#
#  id                        :integer(11)     not null, primary key
#  screen_name               :string(255)     
#  email                     :string(255)     
#  salt                      :string(40)      
#  crypted_password          :string(40)      
#  remember_token            :string(255)     
#  remember_token_expires_at :datetime        
#  created_at                :datetime        
#  updated_at                :datetime        
#

require 'digest/sha1'
class User < ActiveRecord::Base
  # Virtual attribute for the unencrypted password
  attr_accessor :password
  
  # Users have Bundles, and Bundlings connect them to Postings
  has_many :bundles,      :dependent => :delete_all
  has_many :bundlings,    :dependent => :delete_all
  has_many :postings,     :through => :bundlings
  
  # 
  validates_presence_of     :email
  validates_format_of       :email, :with => /\A[\S]+@[\w\d]+\.[.\w\d]{2,}\Z/, :message => "looks a bit odd"
  validates_uniqueness_of   :email, :case_sensitive => false, :allow_blank => true, :message => "belongs to someone already"
  
  validates_presence_of     :password,                   :if => :password_required?
  validates_presence_of     :password_confirmation,      :if => :password_required?, :message => "is missing"
  validates_confirmation_of :password,                   :if => :password_required?, :message => "isn't repeated below"
  validates_length_of       :password, :within => 4..40, :if => :password_required?, :allow_blank => true,
    :too_short => "is too tiny", :too_long => "is too huge"
  
  before_save :encrypt_password
  
  # Prevents a user from submitting a crafted form that bypasses activation
  # Anything users can edit should be added here
  attr_accessible :screen_name, :email, :password, :password_confirmation
  # Authenticates a user by their email and unencrypted password.  Returns the user or nil.
  class << self
    def authenticate(email, password)
      u = find_by_email(email) # need to get the user for the salt
      u && u.authenticated?(password) ? u : nil
    end
  
    # Encrypts some data with the salt.
    def encrypt(password, salt)
      Digest::SHA1.hexdigest("--#{salt}--#{password}-")
    end
  end
  
  # Encrypts the password with the user salt
  def encrypt(password)
    self.class.encrypt(password, salt)
  end
  
  def authenticated?(password)
    crypted_password == encrypt(password)
  end
  
  def remember_token?
    remember_token_expires_at && Time.now.utc < remember_token_expires_at 
  end
  
  # These create and unset the fields required for remembering users between browser closes
  def remember_me
    remember_me_for 2.weeks
  end
  
  def remember_me_for(time)
    remember_me_until time.from_now.utc
  end
  
  def remember_me_until(time)
    self.remember_token_expires_at = time
    self.remember_token            = encrypt("#{email}--#{remember_token_expires_at}")
    save(false)
  end
  
  def forget_me
    self.remember_token_expires_at = nil
    self.remember_token            = nil
    save(false)
  end
  
  # Calculate statistics for all bundles owned by this user
  def bundle_aggregates(recalculate=false)
    
    # return the existing aggregates if it exists and we don't need to recalculate
    return @bundle_aggregates if !recalculate && @bundle_aggregates
    
    labeled_vectors = bundles.find(:all, :include => :current_postings).inject(Hash.new { |h, k| h[k] = [] }) do |memo, bundle|
      bundle.aggregates.each { |k, v| memo[k] << v }
      memo
    end
    
    # calculate averages
    results = labeled_vectors.inject(Hash.new { |h, k| h[k] = 0.0 }) do |memo, vector|
      label, values = vector.first, vector.last
      memo[label] = values.inject(0.0) { |sum, val| sum += val } / values.length
      memo
    end
    
    # round off stats
    results[:credit_score] = results[:credit_score].round
    results[:bid_count] = results[:bid_count].round
    
    # put the results back into the aggregates
    @bundle_aggregates = results
  end
  
protected
  # for before filter 
  def encrypt_password
    return if password.blank?
    self.salt = Digest::SHA1.hexdigest("--#{Time.now.utc}--#{email}--") if new_record?
    self.crypted_password = encrypt(password)
  end
    
  # checks if a password is required because it is blank
  def password_required?
    crypted_password.blank? || !password.blank?
  end
end
